package com.zcareu.logServ;

import com.zcareu.logServ.entity.Role;
import com.zcareu.logServ.entity.User;
import com.zcareu.logServ.repo.RoleRepo;
import com.zcareu.logServ.repo.UserRepo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.PasswordMatcher;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
@ControllerAdvice
@SpringBootApplication
public class LogServApplication {

    private static Logger log = LoggerFactory.getLogger(LogServApplication.class);

    @ExceptionHandler(AuthorizationException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public String handleException(AuthorizationException e, Model model) {

        // you could return a 404 here instead (this is how github handles 403, so the user does NOT know there is a
        // resource at that location)
        log.debug("AuthorizationException was thrown", e);

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", HttpStatus.FORBIDDEN.value());
        map.put("message", "No message available");
        model.addAttribute("errors", map);

        return "error";
    }

    @Bean
    PasswordMatcher credentialsMatcher() {
        return new PasswordMatcher();
    }

    @Bean
    public Realm realm(DataSource dataSource, PasswordMatcher credentialsMatcher) {
        JdbcRealm jdbcRealm = new JdbcRealm();

        jdbcRealm.setDataSource(dataSource);
//		jdbcRealm.setSaltStyle(JdbcRealm.SaltStyle.CRYPT);
        jdbcRealm.setPermissionsLookupEnabled(true);
        jdbcRealm.setCredentialsMatcher(credentialsMatcher);

        return jdbcRealm;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        chainDefinition.addPathDefinition("/filter/**", "authc,authcBasic[permissive],rest[filter]");
        chainDefinition.addPathDefinition("/**", "authc,authcBasic[permissive]");

        return chainDefinition;
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);

        return defaultAdvisorAutoProxyCreator;
    }

    @ModelAttribute(name = "subject")
    public Subject subject() {
        return SecurityUtils.getSubject();
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(LogServApplication.class, args);

        RoleRepo roleRepo = ctx.getBean(RoleRepo.class);

        Role admin = roleRepo.save(new Role("admin", new ArrayList(Arrays.asList("*"))));
        roleRepo.save(new Role("user", new ArrayList(Arrays.asList("filter:read", "log:read"))));

        UserRepo userRepo = ctx.getBean(UserRepo.class);

        PasswordService passwordService = ctx.getBean(PasswordMatcher.class).getPasswordService();

        userRepo.save(new User("root",
                passwordService.encryptPassword("adminadmin"),
                Arrays.asList(admin)));
    }

}
